/******************************************************************************\
    CriticalSection.hpp contains a critical section class and base classes 
	 for locked objects.
    Copyright (C) 2008  Victor Vinogradov (fly3333@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
\******************************************************************************/

#ifndef __CRITICAL_SECTION_HPP
#define __CRITICAL_SECTION_HPP

#include ".\Object.hpp"
#include <windows.h>

class cSubCriticalSection : public cSubObject
{
	CRITICAL_SECTION CriticalSection;
public:
	cSubCriticalSection()
	{
		InitializeCriticalSection( &CriticalSection );
	}
	virtual ~cSubCriticalSection()
	{
		DeleteCriticalSection( &CriticalSection );
	}
	void Enter()
	{
		EnterCriticalSection( &CriticalSection );
	}
	void Leave()
	{
		LeaveCriticalSection( &CriticalSection );
	}


	friend class cCriticalSection;
};

class cLockObject
{
public:
	virtual void Enter() const = 0;
	virtual void Leave() const = 0;
};

class cCriticalSection : public cObject< cSubCriticalSection >, public cLockObject
{
public:
	cCriticalSection()
	{
		cSubCriticalSection *sub_critical_section;
		try
		{
			sub_critical_section = new cSubCriticalSection();
		}
		catch( ... )
		{
		}
		SwitchToSubObject( sub_critical_section );
	}
	virtual void Enter() const
	{
		GetSubObject()->Enter();
	}
	virtual void Leave() const
	{
		GetSubObject()->Leave();
	}
};

class cLocker
{
	const cLockObject &LockObject;
public:
	cLocker( const cLockObject &lock_object )
		: LockObject( lock_object )
	{
		LockObject.Enter();
	}
	~cLocker()
	{
		LockObject.Leave();
	}
};

class cUnlocker
{
	const cLockObject &LockObject;
public:
	cUnlocker( const cLockObject &lock_object )
		: LockObject( lock_object )
	{
		LockObject.Leave();
	}
	~cUnlocker()
	{
		LockObject.Enter();
	}
};

template< class cType >
class cCriticalData
{
	cCriticalSection CriticalSection;
	cType Data;
public:
	cCriticalData( const cType &data/* = cType()/**/ )
		: CriticalSection()
		, Data( data )
	{
	}
	operator cType() const
	{
		cLocker locker( CriticalSection );
		return Data;
	}
	template< class cAnotherType >
	cCriticalData &operator =( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data = data;
		return *this;
	}
	template< class cAnotherType >
	cCriticalData &operator +=( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data += data;
		return *this;
	}
	template< class cAnotherType >
	cCriticalData &operator -=( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data -= data;
		return *this;
	}
	template< class cAnotherType >
	cCriticalData &operator *=( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data *= data;
		return *this;
	}
	template< class cAnotherType >
	cCriticalData &operator /=( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data /= data;
		return *this;
	}
	template< class cAnotherType >
	cCriticalData &operator %=( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data %= data;
		return *this;
	}
	template< class cAnotherType >
	cCriticalData &operator &=( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data &= data;
		return *this;
	}
	template< class cAnotherType >
	cCriticalData &operator ^=( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data ^= data;
		return *this;
	}
	template< class cAnotherType >
	cCriticalData &operator |=( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data |= data;
		return *this;
	}
	template< class cAnotherType >
	cCriticalData &operator <<=( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data <<= data;
		return *this;
	}
	template< class cAnotherType >
	cCriticalData &operator >>=( const cAnotherType &data )
	{
		cLocker locker( CriticalSection );
		Data >>= data;
		return *this;
	}
	cCriticalData &operator ++()
	{
		cLocker locker( CriticalSection );
		++Data;
		return *this;
	}
	cType operator ++( int )
	{
		cLocket locker( CriticalSection );
		return Data++;
	}
};

#endif
